package com.yuntoyun.fwcore.plug.qrcode.zxing;
/*
 * +----------------------------------------------------------------------
 * | @Author: codeavatar   @Year：2021
 * +----------------------------------------------------------------------
 * | @Email: codeavatar@aliyun.com
 * +----------------------------------------------------------------------
 */
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.view.Gravity;
import android.view.View.MeasureSpec;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import java.util.Hashtable;

/**
 * 生成条形码和二维码的工具
 */
public class ZXingUtils {

    /**
     * 黑点颜色
     */
    private static final int BLACK = 0xFF000000;
    /**
     * 白色
     */
    private static final int WHITE = 0xFFFFFFFF;
    /**
     * 正方形二维码宽度
     */
    //private static final int CODE_WIDTH = 440;
    /**
     * LOGO宽度值,最大不能大于二维码20%宽度值,大于可能会导致二维码信息失效
     */
    //private static final int LOGO_WIDTH_MAX = CODE_WIDTH / 5;
    /**
     * LOGO宽度值,最小不能小鱼二维码10%宽度值,小于影响Logo与二维码的整体搭配
     */
    //private static final int LOGO_WIDTH_MIN = CODE_WIDTH / 10;

    private static final int LOGO_WIDTH_MAX_VAL = 10;
    private static final int LOGO_WIDTH_MIN_VAL = 15;

    /**
     * 生成二维码 要转换的地址或字符串,可以是中文
     *
     * @param url
     * @param width
     * @param height
     * @return
     */
    public static Bitmap createQRImage(String url, final int width,
                                       final int height) {
        try {
            // 判断URL合法性
            if (url == null || "".equals(url) || url.length() < 1) {
                return null;
            }
            Hashtable<EncodeHintType, String> hints = new Hashtable<EncodeHintType, String>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            // 图像数据转换，使用了矩阵转换
            BitMatrix bitMatrix = new QRCodeWriter().encode(url,
                    BarcodeFormat.QR_CODE, width, height, hints);
            int[] pixels = new int[width * height];
            // 下面这里按照二维码的算法，逐个生成二维码的图片，
            // 两个for循环是图片横列扫描的结果
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (bitMatrix.get(x, y)) {
                        pixels[y * width + x] = 0xff000000;
                    } else {
                        pixels[y * width + x] = 0xffffffff;
                    }
                }
            }
            // 生成二维码图片的格式，使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height,
                    Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成还Logo的二维码 要转换的地址或字符串,可以是中文
     *
     * @param content
     * @param widthCode
     * @param heightCode
     * @param logoBitmap
     * @return
     */
    public static Bitmap createQRImage(String content, final int widthCode,
                                       final int heightCode, Bitmap logoBitmap) {
        try {
            // 判断content合法性
            if ("".equals(content) || content.length() < 1) {
                return null;
            }

            int logoWidth = logoBitmap.getWidth();
            int logoHeight = logoBitmap.getHeight();
            int logoHaleWidth = logoWidth >= widthCode ? (widthCode / LOGO_WIDTH_MIN_VAL)
                    : (widthCode / LOGO_WIDTH_MAX_VAL);
            int logoHaleHeight = logoHeight >= heightCode ? (heightCode / LOGO_WIDTH_MIN_VAL)
                    : (heightCode / LOGO_WIDTH_MAX_VAL);
            // 将logo图片按martix设置的信息缩放
            Matrix m = new Matrix();
            float sx = (float) 2 * logoHaleWidth / logoWidth;
            float sy = (float) 2 * logoHaleHeight / logoHeight;
            m.setScale(sx, sy);// 设置缩放信息
            Bitmap newLogoBitmap = Bitmap.createBitmap(logoBitmap, 0, 0,
                    logoWidth, logoHeight, m, false);
            int newLogoWidth = newLogoBitmap.getWidth();
            int newLogoHeight = newLogoBitmap.getHeight();
            Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 设置容错级别,H为最高
            hints.put(EncodeHintType.MAX_SIZE, (heightCode / LOGO_WIDTH_MAX_VAL));// 设置图片的最大值
            hints.put(EncodeHintType.MIN_SIZE, (heightCode / LOGO_WIDTH_MIN_VAL));// 设置图片的最小值
            hints.put(EncodeHintType.MARGIN, 1);// 设置白色边距值
            // 生成二维矩阵,编码时指定大小,不要生成了图片以后再进行缩放,这样会模糊导致识别失败
            BitMatrix matrix = new MultiFormatWriter().encode(content,
                    BarcodeFormat.QR_CODE, widthCode, heightCode, hints);
            int width = matrix.getWidth();
            int height = matrix.getHeight();
            int halfW = width / 2;
            int halfH = height / 2;
            // 二维矩阵转为一维像素数组,也就是一直横着排了
            int[] pixels = new int[width * height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    /*
                     * 取值范围,可以画图理解下 halfW + newLogoWidth / 2 - (halfW -
                     * newLogoWidth / 2) = newLogoWidth halfH + newLogoHeight /
                     * 2 - (halfH - newLogoHeight) = newLogoHeight
                     */
                    if (x > halfW - newLogoWidth / 2
                            && x < halfW + newLogoWidth / 2
                            && y > halfH - newLogoHeight / 2
                            && y < halfH + newLogoHeight / 2) {// 该位置用于存放图片信息
                        /*
                         * 记录图片每个像素信息 halfW - newLogoWidth / 2 < x < halfW +
                         * newLogoWidth / 2 --> 0 < x - halfW + newLogoWidth / 2
                         * < newLogoWidth halfH - newLogoHeight / 2 < y < halfH
                         * + newLogoHeight / 2 -->0 < y - halfH + newLogoHeight
                         * / 2 < newLogoHeight
                         * 刚好取值newLogoBitmap。getPixel(0-newLogoWidth
                         * ,0-newLogoHeight);
                         */
                        pixels[y * width + x] = newLogoBitmap.getPixel(x
                                - halfW + newLogoWidth / 2, y - halfH
                                + newLogoHeight / 2);
                    } else {
                        pixels[y * width + x] = matrix.get(x, y) ? BLACK
                                : WHITE;// 设置信息
                    }
                }
            }

            // 生成二维码图片的格式，使用ARGB_8888
            Bitmap bitmap = Bitmap.createBitmap(width, height,
                    Config.ARGB_8888);
            bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
            return bitmap;
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成条形码
     *
     * @param context
     * @param contents      需要生成的内容
     * @param desiredWidth  生成条形码的宽带
     * @param desiredHeight 生成条形码的高度
     * @param displayCode   是否在条形码下方显示内容
     * @return
     */
    public static Bitmap creatBarcode(Context context, String contents,
                                      int desiredWidth, int desiredHeight, boolean displayCode) {
        Bitmap ruseltBitmap = null;
        /**
         * 图片两端所保留的空白的宽度
         */
        int marginW = 20;
        /**
         * 条形码的编码类型
         */
        BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128;

        if (displayCode) {
            Bitmap barcodeBitmap = encodeAsBitmap(contents, barcodeFormat,
                    desiredWidth, desiredHeight);
            Bitmap codeBitmap = creatCodeBitmap(contents, desiredWidth + 2
                    * marginW, desiredHeight, context);
            ruseltBitmap = mixtureBitmap(barcodeBitmap, codeBitmap, new PointF(
                    0, desiredHeight));
        } else {
            ruseltBitmap = encodeAsBitmap(contents, barcodeFormat,
                    desiredWidth, desiredHeight);
        }

        return ruseltBitmap;
    }

    /**
     * 生成条形码的Bitmap
     *
     * @param contents      需要生成的内容
     * @param format        编码格式
     * @param desiredWidth
     * @param desiredHeight
     * @return
     * @throws WriterException
     */
    protected static Bitmap encodeAsBitmap(String contents,
                                           BarcodeFormat format, int desiredWidth,
										   int desiredHeight) {
        final int WHITE = 0xFFFFFFFF;
        final int BLACK = 0xFF000000;

        MultiFormatWriter writer = new MultiFormatWriter();
        BitMatrix result = null;
        try {
            result = writer.encode(contents, format, desiredWidth,
                    desiredHeight, null);
        } catch (WriterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        int width = result.getWidth();
        int height = result.getHeight();
        int[] pixels = new int[width * height];
        // All are 0, or black, by default
        for (int y = 0; y < height; y++) {
            int offset = y * width;
            for (int x = 0; x < width; x++) {
                pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
            }
        }

        Bitmap bitmap = Bitmap.createBitmap(width, height,
                Config.ARGB_8888);
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
        return bitmap;
    }

    /**
     * 生成显示编码的Bitmap
     *
     * @param contents
     * @param width
     * @param height
     * @param context
     * @return
     */
    protected static Bitmap creatCodeBitmap(String contents, int width,
                                            int height, Context context) {
        TextView tv = new TextView(context);
        LayoutParams layoutParams = new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        tv.setLayoutParams(layoutParams);
        tv.setText(contents);
        tv.setHeight(height);
        tv.setGravity(Gravity.CENTER_HORIZONTAL);
        tv.setWidth(width);
        tv.setDrawingCacheEnabled(true);
        tv.setTextColor(Color.BLACK);
        tv.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());

        tv.buildDrawingCache();
        Bitmap bitmapCode = tv.getDrawingCache();
        return bitmapCode;
    }

    /**
     * 将两个Bitmap合并成一个
     *
     * @param first
     * @param second
     * @param fromPoint 第二个Bitmap开始绘制的起始位置（相对于第一个Bitmap）
     * @return
     */
    protected static Bitmap mixtureBitmap(Bitmap first, Bitmap second,
                                          PointF fromPoint) {
        if (first == null || second == null || fromPoint == null) {
            return null;
        }
        int marginW = 20;
        Bitmap newBitmap = Bitmap.createBitmap(
                first.getWidth() + second.getWidth() + marginW,
                first.getHeight() + second.getHeight(), Config.ARGB_4444);
        Canvas cv = new Canvas(newBitmap);
        cv.drawBitmap(first, marginW, 0, null);
        cv.drawBitmap(second, fromPoint.x, fromPoint.y, null);
        //cv.save(Canvas.ALL_SAVE_FLAG);
        cv.save();
        cv.restore();

        return newBitmap;
    }

}
